/*
 * Copyright 2010 Marcin Blazejewski
 *
 * This file is part of Nofate.
 *
 * Nofate is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Nofate is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Nofate. If not, see <http://www.gnu.org/licenses/>.
 *
 */


#include "typebase.hpp"
#include "thing.hpp"
#include "attribute.hpp"
#include <redeye/redeye.h>

#include <map>
#include <string>

#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <iostream>

using namespace std;


/**
 * typebase data structure
 * typebase = {
 *   id1 : { "basic" : BP_basic, 
 *           "attr1" : ( attr_constructor, N, "a1.1", "a1.2", ... "a1.N" ), 
 *           "attr2" : ( ... ) },
 *   id2 : { ... }
 * }
 */

map<string, map<string, void *> > typebase;


BP_basic*   bp_basic(char ch, int fg, char alpha, char type);
void**      bp_attr(Attr*(f)(const void**), int n, ...);

int tb_init() {
    map<string, void*> bp;
    
    bp.clear();
    bp["basic"] = bp_basic(' ', 0x00000000, 0, TH_TYPE_TERRAIN);
    bp["terrain"] = bp_attr(atld_terrain, 1, (void*)0x0023A040);
    typebase["ground"] = bp;

    bp.clear();
    bp["basic"] = bp_basic('T', 0x0078FFAA, 40, TH_TYPE_TERRAIN);
    bp["terrain"] = bp_attr(atld_terrain, 1, (void*)0x0062A03B);
    typebase["tree"] = bp;

    bp.clear();
    bp["basic"] = bp_basic(' ', 0x0, 0, TH_TYPE_TERRAIN);
    bp["terrain"] = bp_attr(atld_terrain, 1, (void*)0x2AAA11CC);
    typebase["floor.stone"] = bp;

    bp.clear();
    bp["basic"] = bp_basic('#', 0x00000080, 80, TH_TYPE_TERRAIN);
    bp["terrain"] = bp_attr(atld_terrain, 1, (void*)0x2AAA11CC);
    typebase["wall"] = bp;

    bp.clear();
    bp["basic"] = bp_basic('%', 0x00000080, 0, TH_TYPE_TERRAIN);
    bp["terrain"] = bp_attr(atld_terrain, 1, (void*)0x2AAA11CC);
    typebase["ruin"] = bp;

    bp.clear();
    bp["basic"] = bp_basic('<', 0x0, 0, TH_TYPE_TERRAIN);
    bp["terrain"] = bp_attr(atld_terrain, 1, (void*)0x2AAA11CC);
    typebase["stairs.down"] = bp;

    bp.clear();
    bp["basic"] = bp_basic('@', 0x00F0A0A0, 50, TH_TYPE_CREATURE);
    typebase["human"] = bp;

    bp.clear();
    bp["basic"] = bp_basic('t', 0x004490C0, 50, TH_TYPE_THING);
    typebase["torch"] = bp;

    bp.clear();
    bp["basic"] = bp_basic(']', 0x00845260, 50, TH_TYPE_THING);
    bp["slot"] = bp_attr(atld_int, 1, (void*)EQS_ARMOUR);
    typebase["mail"] = bp;

    return 0;
}

void tb_clean() {
}

Thing *tb_spawn(const char *id, int ter_bg) {
    map<string, map<string, void *> > :: iterator i;
    map<string, void *> :: iterator j;

    i = typebase.find(string(id));
    if (i == typebase.end()) {
        cerr << "[ERROR] Typebase: no blueprint for '" << id << "'" << endl;
        return NULL;
    }

    BP_basic *basic = (BP_basic*)i->second["basic"];
    Attr* (*f)(void**);

    // construct the object
    Thing *t = NULL;
    if (basic->type == TH_TYPE_THING) t = new Thing(id, basic->alpha, basic->ch, basic->fg);
    else if (basic->type == TH_TYPE_TERRAIN) t = new Thing(id, basic->alpha, basic->ch, basic->fg);
    else if (basic->type == TH_TYPE_CREATURE) t = new Creature(id, basic->alpha, basic->ch, basic->fg);
    else return NULL;
    t->type = basic->type;

    // add attributes from the blueprint
    for (j = i->second.begin(); j != i->second.end(); j++) {
        if (j->first.compare("basic") == 0) continue;

        void **data = (void**)j->second;
        f = (Attr*(*)(void**))data[0];
        Attr *a = f(&data[1]);
        const string &key(j->first);

        t->attributes[ key ] = a;
    }

    // for terrains: override bg color if requested
    if (ter_bg != -1) {
        Aterrain* ter = (Aterrain*)t->attributes["terrain"];
        if (ter) ter->bg = ter_bg;
    }
    
    return t;
}

/******************************************************************************/
/* internals */

BP_basic* bp_basic(char ch, int fg, char alpha, char type) {
    BP_basic *basic = (BP_basic*)malloc(sizeof(BP_basic));
    memset(basic, 0, sizeof(BP_basic));

    basic->ch = ch;
    basic->fg = fg;
    basic->alpha = alpha;
    basic->type = type;
    
    return basic;
}

void** bp_attr(Attr*(f)(const void**), int n, ...) {
    va_list ap;
    int i;
    void **d = (void**)malloc((++n) * sizeof(void*));
    d[0] = (void*)f;

    va_start(ap, n);
    for (i = 1; i < n; i++) {
        d[i] = va_arg(ap, void*);
    }

    return d;
}


/**********************************************************************************/


void mk_hsv_range(unsigned int *v, unsigned int size, 
                  unsigned short h0, unsigned short h1,
                  unsigned char s0, unsigned char s1,
                  unsigned char v0, unsigned char v1,
                  bool hsv_format) {

    float h_step = (float)(h1 - h0) / size;
    float s_step = (float)(s1 - s0) / size;
    float v_step = (float)(v1 - v0) / size;

    for (int i = 0; i < size; i++) {
        if (hsv_format) {
            v[i] = (unsigned short)(h0 + h_step * i) << 16;
            v[i] |= (unsigned char)(s0 + s_step * i) << 8;
            v[i] |= (unsigned char)(v0 + v_step * i);
        }
        else {
            v[i] = hsv2rgb( h0 + h_step * i, s0 + s_step * i, v0 + v_step * i );
        }
    }
}


/**********************************************************************************/

